iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 4
1
AI & Data

Puppeteer 簡單快速建立自己的 Nodejs 爬蟲系列 第 4

Day 4 擷取 分類 儲存

  • 分享至 

  • xImage
  •  

再上一篇我們把資料擷取下來了
接下來我們把他分類與儲存吧

我們可以看到我們擷取下來的資料長這樣
這樣這資料完全沒有規律沒辦法分類

Go day 4 (variables)大家來排隊,一個一個串起來~超展開 – 深入冰山Day4 Hoisting & Scope ChainHow to install Solus OS Using VMware ?團隊建立AMP(Lession 4) - 字型03. ICO 網站多國語言功能JS30-Day44.繼承抽象類別,避免重複方法。

我們看到他一次抓到很多比資料他一定是某種集合
所以我們可以使用cheerio提供的each去跑一件一件

await $('div #ir-list div div ul.list-unstyled.ir-lists li.ir-list h3.ir-list__title a').each((i,el)=>{
        console.log($(el).text())
    })

把原本的改掉後儲存並執行
就可以看到結果變成這樣
每一筆資料都分別的出來了

Go day 4 (variables)
大家來排隊,一個一個串起來~
超展開 – 深入冰山
Day4 Hoisting & Scope Chain
How to install Solus OS Using VMware ?
團隊建立
AMP(Lession 4) - 字型
03. ICO 網站多國語言功能
JS30-Day4
4.繼承抽象類別,避免重複方法。

但抓到這樣我們忘了一個東西
就是瀏覽數
當然我們可以跑一次迴圈再抓一次資料
但這樣是比較沒有效率的
所以我們又要修改原本的程式
同時抓兩個資料

我們可以看到這頁我們要的資料都在這欄 li 底下

我們就把TAG往前推到 il 就好
並把程式改成這樣

//大家可以往下看留言
//感謝 marlin12 提供更好的解法
await $('div #ir-list div div ul.list-unstyled.ir-lists li.ir-list').each((i,el)=>{
       //我們把裏面的el再用cheerio load給其他變數,方便拿取我們要的資料
       let $2 = await cheerio.load($(el).html())
    })

接這我們可以透過 $2 這個變數下去抓取我們要的更多資料
並把資料儲存起來
會長這樣

    await $('div #ir-list div div ul.list-unstyled.ir-lists li.ir-list').each((i,el)=>{
       let $2 =  cheerio.load($(el).html())
       let title = $2('h3.ir-list__title a').text().trim() //text是抓取文字, trim是去頭尾空字串
       /**majo2013


                    於 2018-10-04 發表 | 39 次瀏覽*/
        //原本我們的view會抓到像上面的一樣的文字,我們先去頭尾空字串,再用split去分割瀏覽前得特殊符號取得第一個元素
       let view = $2('div.ir-list__info').text().trim().split('|')[1]
       //順便把文章連結 類別抓下來吧
       let category = $2('div a div.group-badge__name').text().trim()
       let herf = $2('h3.ir-list__title a').attr('href') //attr是抓取 a 裏面的href元素
       console.log('title '+ title +'\n view '+view+'\n category '+category+'\n href '+herf)
    })

大家執行一遍後我們要的資料已經都抓下來了
接著我們要儲存資料

    //先再外面建立陣列
    let data = []
    await $('div #ir-list div div ul.list-unstyled.ir-lists li.ir-list').each( (i, el) => {
        let $2 =  cheerio.load($(el).html())
        //裏面建立一個物件並把資料都放進去
        //這是物件 不了解的可以搜尋了解一下
        let tmp = {
            category:  $2('div a div.group-badge__name').text().trim(),
            title:  $2('h3.ir-list__title a').text().trim(),
            view:  $2('div.ir-list__info').text().trim().split('|')[1],
            herf:  $2('h3.ir-list__title a').attr('href')
        }
        data.push(tmp)
    })
    console.log(data)

有人可能會有些疑問,為甚麼裏面不用async await
因為整個each包在一個await下 如果再放的話會產生非同步
還是不理解的話可以搜尋一下
或者再下面留言,讓我知道我需不需要再補一篇async await的解釋文章
如果要寫nodejs async await的概念一定要理解清楚,不然將來會吃很多虧(bugs)


接著我們把檔案透過node本身內建的fs儲存下來
今天程式碼就長這樣

const puppeteer = require('puppeteer');
const cheerio = require('cheerio');
(async () => {
    const browser = await puppeteer.launch({
        headless: true
    });
    const page = await browser.newPage();
    await page.goto('https://ithelp.ithome.com.tw/ironman');

    //先等待網頁載入到footer,不然有時候執行太快抓不到網頁
    await page.waitForSelector('section')
    //把網頁的body抓出來
    let body = await page.content()

    //接著我們把他丟給cheerio處理
    let $ = await cheerio.load(body)

    //先再外面建立陣列
    let data = []
    await $('div #ir-list div div ul.list-unstyled.ir-lists li.ir-list').each((i, el) => {
        let $2 = cheerio.load($(el).html())
        //裏面建立一個物件並把資料都放進去
        //這是物件 不了解的可以搜尋了解一下
        let tmp = {
            category: $2('div a div.group-badge__name').text().trim(),
            title: $2('h3.ir-list__title a').text().trim(),
            view: $2('div.ir-list__info').text().trim().split('|')[1],
            herf: $2('h3.ir-list__title a').attr('href')
        }
        data.push(tmp)
    })

    const fs = require('fs');
    const content = JSON.stringify(data); //轉換成json格式
    fs.writeFile("ithome.json", content, 'utf8', function (err) {
        if (err) {
            return console.log(err);
        }
        console.log("The file was saved!");
    });

    await browser.close()
})();

大家在同個資料夾底下就可以看到我們抓下來的檔案

今天我們只抓到第1頁資料
下一篇
我們要把所有文章都抓下來
當然如果有興趣自己找方法也很快


上一篇
Day 3 爬爬爬 鐵人幫
下一篇
Day 5 擷取所有文章資料
系列文
Puppeteer 簡單快速建立自己的 Nodejs 爬蟲25
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

1
marlin12
iT邦研究生 5 級 ‧ 2018-10-06 21:25:59

cheerio.load($(el).html())會把整個el的html重新載入,拖慢速度。改用$(el).find()可以提速2至3倍。

let data = [];
$('ul.ir-lists li.ir-list').each((i, el) => {
    const $el = $(el);
    const info = {
        category : $el.find('div.group-badge__name').text().trim(),
        title    : $el.find('h3.ir-list__title a').text().trim(),
        view     : $el.find('div.ir-list__info').text().split('|')[1].trim(),
        href     : $el.find('h3.ir-list__title a').attr('href')
    };
    
    data.push( info );
});

我要留言

立即登入留言